#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <getopt.h>
#include <config.h>

#include "error.h"
#include "verbs.h"
#include "main.h"
#include "configuration.h"

global_args GLOBAL_ARGS = {
    NULL, NULL, NULL, NULL, NULL, NULL, 0
};

int _argc;
char** _argv;

_Noreturn void usage(FILE *restrict out) {
    fputs("Usage: " PROGRAM_NAME " [OPTIONS] COMMAND ...\n", out);
    fputs("Login to XTU school network.\n", out);
    fputs("\n", out);
    fputs("Options:\n", out);
    fputs("  -b, --bind=INTERFACE       bind all outgoing requests to INTERFACE\n", out);
    fputs("  -c, --config=FILE          use FILE as configuration file\n", out);
    fputs("  -h, --host=HOST            use the HOST to login\n", out);
    fputs("  -s, --search=TEXT          pass the search string TEXT\n", out);
    fputs("\n", out);
    fputs("      --help                 display this help and exit\n", out);
    fputs("  -v, --verbose              display verbose infomation\n", out);
    fputs("      --version              output version information and exit\n", out);
    fputs("\n", out);
    fputs("Combined commands:\n", out);
    fputs("      try-login              probe the network connectivity and log in if unlogined\n", out);
    fputs("      try-logout             try to log out the network\n", out);
    fputs("\n", out);
    fputs("Single commands:\n", out);
    fputs("      probe                  probe the network connectivity\n", out);
    fputs("      pageinfo               get the login page infomation\n", out);
    fputs("      encrypt                encrypt the password\n", out);
    fputs("      login                  log in the network\n", out);
    fputs("      logout                 log out the network\n", out);
    fputs("      onlineinfo             get online status infomation\n", out);
    exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
}

_Noreturn void version() {
    puts(PACKAGE_STRING);
    puts("Copyright (C) 2022 fifth_light.");
    puts("License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.");
    puts("This is free software: you are free to change and redistribute it.");
    puts("There is NO WARRANTY, to the extent permitted by law.");
    puts("");
    puts("Written by fifth_light.");
    exit(EXIT_SUCCESS);
}

int main(int argc, char *argv[]) {
    _argc = argc;
    _argv = argv;

    static struct option long_options[] = {
        {"bind",    required_argument, NULL,  'b' },
        {"config",  required_argument, NULL,  'c' },
        {"host",    required_argument, NULL,  'h' },
        {"search",  required_argument, NULL,  's' },
        {"help",    no_argument,       NULL,  'H' },
        {"verbose", no_argument,       NULL,  'v' },
        {"version", no_argument,       NULL,  'V' },
        {0,         0,                 0,  0 }
    };
    
    int config_read_status = read_configuration();
    if (config_read_status > 0) {
        if (parse_configuation()) {
            error(EXIT_FAILURE, 0, "Configuration parsing failed.");
        }
        free_configuation();
    } else if (config_read_status < 0) {
        exit(EXIT_FAILURE);
    }
    
    while (1) {

        int c = getopt_long(argc, argv, "-c:b:h:s:v", long_options, NULL);

        if (c == -1) {
            error(0, 0, "No command supplied.");
            usage(stderr);
        }

        switch (c) {
            case 'b':
                GLOBAL_ARGS.bind_interface = optarg;
                break;

            case 'c':
                GLOBAL_ARGS.config_file = optarg;
                break;

            case 'H':
                usage(stdout);
                break;

            case 'V':
                version();
                break;

            case 'h':
                GLOBAL_ARGS.portal_host = optarg;
                break;

            case 's':
                GLOBAL_ARGS.portal_search = optarg;
                break;

            case 'v':
                (*(int*) &GLOBAL_ARGS.verbose_level)++;
                break;

            case 1:
                const Verb* verb = verbs_find_verb(optarg, ALL_VERBS);
                if (verb != NULL) {
                    int new_argv_size = argc - optind + 1;
                    char** new_argv = (char**) calloc(new_argv_size, sizeof(char*));
                    new_argv[0] = argv[0];
                    for (int i = 0; i < new_argv_size - 1; i++) {
                        new_argv[i + 1] = argv[i + optind];
                    }
                    optind = 0;
                    exit(verb -> dispatcher(new_argv_size, new_argv));
                } else {
                    error(0, 0, "Unknown command: %s", optarg);
                    usage(stderr);
                }
                break;

            default:
                usage(stderr);
                break;
        }
    }

    assert(0);
    exit(1);
    return EXIT_FAILURE;
}
